#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<iostream>
#include<thread>
#include"include/Message.h"
#include "DBHelper.h"

struct SockInfo{
    sockaddr_in addr;
    int fd;
    int status;
};

SockInfo infos[512];//设置连接数
void* working(void* arg);

int main(){

    /**
     * 1.创建监听的套接字 socket()
     * AF_INET: 使用IPv4格式的ip地址
     * SOCK_STREAM: 使用流式的传输协议
     * SOCK_STREAM或0: 流式传输默认使用的是tcp
    */
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd==-1){//判断是否创建成功
        std::cout<<"创建失败！！！"<<std::endl;
        return -1;
    }
    /**
     * 2.绑定本地的IP与Port bind()
     * 第一个参数：监听的文件描述符
     * 第二个参数：sockaddr结构体指针
     * 第三个参数：结构体指针指向的内存大小
    */
    sockaddr_in saddr;
    saddr.sin_family = AF_INET;//设置协议为IPv4
    saddr.sin_port = htons(9999);//设置端口号，需要转成大端短整形，默认是小端
    /**
     * INADDR_ANY = 0 相当于 0.0.0.0 
     * 设置为0就不需要进行ip地址的大端转化
     * 设置为0会自动识别本地IP
    */
    saddr.sin_addr.s_addr = INADDR_ANY; 
    int ref = bind(fd,(sockaddr*)&saddr,sizeof(saddr));
    if(ref==-1){
        std::cout<<"绑定失败！！！"<<std::endl;
        return -1;
    }
    /**
     * 3.设置监听 listen()
     * 参数1：文件描述符，在监听之前必须要绑定 bind()
     * 参数2：同时能处理的最大连接要求，最大值为128
    */
    ref = listen(fd,128);
    if(ref==-1){
        std::cout<<"监听失败！！！"<<std::endl;
        return -1;
    }

    //初始化连接数组
    int max = sizeof(infos)/sizeof(infos[0]);
    for(int i=0;i<max;i++){
        bzero(&infos[i],sizeof(infos[i]));//设置为0
        infos[i].fd=-1;
    }
    std::cout<<"服务器启动成功！！！"<<std::endl;
    /**
     * 4.建立连接 accept()
     * 参数1：文件描述符
     * 参数2：sockaddr结构体指针,传出参数，存储建立连接的客户端的地址信息
     * 参数3：结构体指针指向的内存大小 引用类型
    */
    
    unsigned int addrlen = sizeof(sockaddr_in);
    while(true){
        SockInfo* pinfo;
        for(int i=0;i<max;i++){
            if(infos[i].fd==-1){
                pinfo=&infos[i];
                break;
            }
        }

        int cfd = accept(fd,(sockaddr*)&pinfo->addr,&addrlen);
        pinfo->fd = cfd;
        if(cfd==-1){
            std::cout<<"建立连接失败!!!"<<std::endl;
            break;
        }
        std::cout<<"连接成功！！！\n";
        //创建子线程
        std::thread t(working,pinfo);
        t.detach();
        // pthread_t tid;
        // pthread_create(&tid,NULL,working,pinfo);
        // pthread_detach(tid);//使用detach分离 join会阻塞
    }
    close(fd);
    return 0;
}





void* working(void* arg){ 
    SockInfo* pinfo = (SockInfo*)arg;
    //连接建立成功，输出相关信息，并转化为小端
    char ip[32];
    std::cout<<"IP:"<<inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,ip,sizeof(ip))<<"端口："<<ntohs(pinfo->addr.sin_port)<<std::endl;
    /**
     * 5.通信
    */
    bool Flag = false;
    std::cout<<"接收内容中..."<<std::endl;
    std::cout<<"pinfo->fd："<<pinfo->fd<<std::endl;
    DBHelper mysqls;
    mysqls.ConnectDatabase();
    Flag = true;
    std::cout<<"数据库已连接..."<<std::endl;
    while(true){
        Message message ;
        memset(&message,0,sizeof(message));//清空缓存 
        
        
        //char buffer[1024];
        /**recv()
         * 参数1：用于通信的文件描述符, accept() 函数的返回值
         * 参数2：指向一块有效内存, 用于存储接收是数据
         * 参数3：参数buf指向的内存的容量
        */
        
        int len = recv(pinfo->fd,&message,sizeof(message),0);
        if(len>0){
            
            //memcpy(&message, buffer, sizeof(buffer));
            //std::cout<<"客户端发送内容："<<message.sno<<" "<<message.username<<" "<<message.userpassword<<" "<<message.email<<std::endl;
            if(message.username!=0&&message.userpassword!=0&&message.flag==1){
                pinfo->status=1;
                //std::cout<<"客户端发送内容："<<message.username<<" "<<message.userpassword<<std::endl;
                //int flag = 0;
                if (mysqls.QueryDatabase(message.username, message.userpassword) == true){ 
                    memset(&message,0,sizeof(message));//清空缓存 
                    message.isLogin = 1;
                    message.flag=1;
                    send(pinfo->fd,&message,sizeof(message),0);	
                    
				}
                else{   
                    memset(&message,0,sizeof(message));//清空缓存
                    message.isLogin = 0;
                    message.flag=1;
					send(pinfo->fd,&message,sizeof(message),0);	
                }
            }
            else if(message.sno!=0&&message.username!=0&&message.userpassword!=0&&message.email!=0&&message.flag==2){
                pinfo->status=2;
                if (mysqls.InsertData(message.sno,message.username, message.userpassword,message.email) == true){
                    
                    memset(&message,0,sizeof(message));//清空缓存
                    message.isReg = 1;
                    message.flag=2; 
					send(pinfo->fd,&message,sizeof(message),0);
				}
                else{
                    memset(&message,0,sizeof(message));//清空缓存
                    message.isReg = 0;
                    message.flag=2;
					send(pinfo->fd,&message,sizeof(message),0);	
                }
            }else if(message.flag==3){
                pinfo->status=3;
                for(int i=0;i<512;i++){   
                    if(infos[i].fd!=-1&&infos[i].fd!=pinfo->fd&&infos[i].status==3){
                        //std::cout << "其它玩家客户端蛇的长度：" << message.length << std::endl;
                        send(infos[i].fd,&message,sizeof(message),0);
                        //sleep(1);
                    }
                    
                }
                
            }
            
            /**send()
             * 参数1：通信的文件描述符, accept() 函数的返回值
             * 参数2：传入参数, 要发送的字符串
             * 参数3：要发送的字符串的长度
            */
            
        }else if(len==0){
            std::cout<<"客户端断开连接..."<<std::endl;
            break;
        }else{
            for(int i=0;i<512;i++){
                memset(&message,0,sizeof(message));//清空缓存   
                if(infos[i].fd!=-1&&infos[i].fd!=pinfo->fd){
                    send(infos[i].fd,&message,sizeof(message),0);
                    //sleep(1);
                }
                    
            }
            std::cout<<"recv函数执行出错!!!"<<std::endl;
            break;
        }
        
    }

    mysqls.FreeConnect();
    std::cout<<"数据库已断开..."<<std::endl;
    //关闭所有文件描述符
    close(pinfo->fd);
    pinfo->fd=-1;
    return NULL;
}